﻿using TestingConcurrencyBenchmark.Helpers;

namespace TestingConcurrencyBenchmark.Problems.ReadersWriters
{
    /// <summary>
    /// A pattern found by Alan Downey
    /// The first thread to lock a semaphore using this lightswitch makes the semaphore wait
    /// The next threads will not touch the semaphore
    /// The last thread to call Unlock will signal the semaphore again.
    /// 
    /// Consider a room of people:
    /// - The first person to enter hits the lightswitch so the lights go on.
    /// - When other people enter, they won't touch the lightswitch as the light is already on
    /// - The last person to leave will turn the lights off again.
    /// </summary>
    internal class LightSwitch
    {
        private readonly BenchmarkSemaphore _mutex;
        private int _counter;

        public LightSwitch()
        {
            _mutex = new BenchmarkSemaphore(1);
        }

        /// <summary>
        /// "Turns on the light" -> Calls benchmarkSemaphore.WaitOne() if the thread is the first to enter
        /// Any other thread that is not the first one in will just update the counter.
        /// </summary>
        /// <param name="benchmarkSemaphore">the semaphore to call WaitOne() on if a thread is the first to enter</param>
        public void Lock(BenchmarkSemaphore benchmarkSemaphore)
        {
            _mutex.WaitOne();
            _counter += 1;
            if (_counter == 1) benchmarkSemaphore.WaitOne();
            _mutex.Release();
        }

        /// <summary>
        /// "Turns off the light" -> Calls benchmarkSemaphore.Release() if the thread is the last to leave
        /// Any other thread that is not the last one in will just update the counter
        /// </summary>
        /// <param name="benchmarkSemaphore">the samephore to call Release() on if a thread is the last one to leave</param>
        public void Unlock(BenchmarkSemaphore benchmarkSemaphore)
        {
            _mutex.WaitOne();
            _counter -= 1;
            if (_counter == 0) benchmarkSemaphore.Release();
            _mutex.Release();
        }
    }
}